﻿using RayDen.Library.Components.SystemComponents;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity;
using RayDen.Library.Entity.Frames;
using RayDen.Library.Entity.Scene;
using RayDen.Library.Entity.SceneGraph;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Data;
using RayDen.RayEngine.Engines.ManyLights;

namespace RayDen.RayEngine.Scene
{
    public abstract class SceneBuilderComponentBase<TElement> : DependentComponentBase, INodeVisitor, ISceneBuilder
        where TElement : SceneGraphElement
    {
        internal RayEngineScene Owner
        {
            get { return this.BaseContainer.Get<RayEngineScene>(); }
        }

        internal SceneGraphInfo SceneGraph
        {
            get { return this.BaseContainer.Get<SceneGraphInfo>(); }
        }

        public SceneBuilder Builder { get; set; }



        public void Setup(RayEngineScene scn, SceneGraphInfo grp)
        {
            this.BaseContainer.Populate(scn, grp);
        }

        public void Visit(SceneGraphElement node)
        {
            if (node is TElement)
                this.VisitInternal((TElement)node);
        }

        protected abstract void VisitInternal(TElement node);
    }

    public class MaterialsBuilder : SceneBuilderComponentBase<SceneMaterialDataNode>
    {
        protected override void VisitInternal(SceneMaterialDataNode node)
        {
            var mi = (MaterialInfo)node.GetDataItem();
            this.Builder.MaterialInfos.Add(mi);
        }
    }

    public class MeshBuilder : SceneBuilderComponentBase<GeometryInstanceNode>
    {
        protected override void VisitInternal(GeometryInstanceNode node)
        {
            var geoData = (GeometryInfo)SceneGraph.ResolveDataNode<GeometryElementNode>(node).GetDataItem();
            //Transform
        }
    }

    public class GeoDataBuilder : SceneBuilderComponentBase<GeometryGroupNode>
    {
        protected override void VisitInternal(GeometryGroupNode node)
        {
            var sceneData = (SceneGeometryInfo)SceneGraph.ResolveDataNode<SceneGeometryDataNode>(node).GetDataItem();
            this.Builder.SceneGeo = sceneData;
        }
    }


    public class LightBuilder : SceneBuilderComponentBase<LightsourceNode>
    {
        private LightEmissionProfilesManager lm;

        public LightBuilder()
        {
            lm = LightEmissionProfilesManager.Instance;
        }

        protected override void VisitInternal(LightsourceNode node)
        {
            var li = (LightsourceInfo)node.GetDataItem();
            ILight light = null;
            EmissionProfile defaultProfile = lm[li.ProfileName ?? "d5000"];
            switch (li.Type)
            {
                case LightSourceTypes.Area:
                    var meshLight = new MeshLight()
                    {
                        gain = new RgbSpectrum(li.Gain),
                        MeshName = li.MeshName,
                        Profile = defaultProfile,
                        spectra = SPD_Data.FromSampled(SPD_Data.A, SpectrumType.Illuminant),
                        LightName = li.MeshName
                    };
                    light = meshLight;
                    break;
                case LightSourceTypes.Sky:
                    if (this.Builder.EnvironmentMap == null)
                    {
                        this.Builder.EnvironmentMap =
                            new SunLight(li.Turbulence, li.Direction)
                            {
                                Profile = defaultProfile
                            }; ;
                        //new SkyLight(li.Turbulence, li.Direction);
                        light = this.Builder.EnvironmentMap;
                    }
                    break;

                case LightSourceTypes.EnvironmentMap:
                    if (this.Builder.EnvironmentMap == null)
                    {
                        this.Builder.EnvironmentMap = new InfiniteLight(li.ImageName)
                            {
                                Profile = defaultProfile
                            };
                        light = this.Builder.EnvironmentMap;
                    }
                    break;
                case LightSourceTypes.Point:
                    light = 
                        //new SphereAreaLight(li) { Profile = defaultProfile };

                        new PointLight( (Point)li.Position, new RgbSpectrum(li.Spectra),SPD_Data.FromSampled(SPD_Data.D5000, SpectrumType.Illuminant)) { Profile = defaultProfile };

                    break;
            }
            if (light != null)
            {
                if (!this.Builder.Lights.ContainsKey(light.Name))
                    this.Builder.Lights.Add(light.Name, light);
            }
            Tracer.TraceLine("Light {0} Emission Profile {1}", light.GetType().Name, light.Profile.Name);
        }
    }

    public class CameraBuilder : SceneBuilderComponentBase<CameraNode>
    {
        protected override void VisitInternal(CameraNode node)
        {
            this.Builder.Cameras.Add((CameraInfo)node.GetDataItem());
        }
    }


    public class VolumeBuilder : SceneBuilderComponentBase<VolumeNode<VolumeMaterialInfo>>
    {
        protected override void VisitInternal(VolumeNode<VolumeMaterialInfo> node)
        {
            this.Builder.VolumeMats.Add((VolumeMaterialInfo)node.GetDataItem());
        }
    }
}